package com.threader.utils;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;

public class StringUtils {
	
	private static final char[] HTML_ENTITIES =       {'&',     '<',    '>',    '\"','\''};
	private static final String[] HTML_ESC_ENTITIES = {"&amp;", "&lt;", "&gt;", "&quot;","&#39;"};
	private static final char[] JS_HTML_ENTITIES =       {'&',     '<',    '>',    '\"'};
	private static final String[] JS_HTML_ESC_ENTITIES = {"&amp;", "&lt;", "&gt;", "&quot;"};
	private static List htmlEscapeIndex;
	private static List javascriptEscapeIndex;
	private static List attachmentFilenameIndex = null;
	
	static {
		htmlEscapeIndex = new ArrayList();
		for (int i = 0; i < HTML_ENTITIES.length; i++) {
			int cval = HTML_ENTITIES[i];
			while (htmlEscapeIndex.size() < cval + 1) {
				htmlEscapeIndex.add(null);
			}
			htmlEscapeIndex.set(cval, HTML_ESC_ENTITIES[i]);
		}
		
		javascriptEscapeIndex = new ArrayList();
		for (int i = 0; i < JS_HTML_ENTITIES.length; i++) {
			int dval = JS_HTML_ENTITIES[i];
			while (javascriptEscapeIndex.size() < dval + 1) {
				javascriptEscapeIndex.add(null);
			}
			javascriptEscapeIndex.set(dval, JS_HTML_ESC_ENTITIES[i]);
		}
		
	}
	
	// this sets up a similar list of replacement strings, except that all the strings are "_"
	// if i really wanted to be tricky, could use a BitSet
	private static synchronized List getAttachmentFilenameReplacements() {
		if (attachmentFilenameIndex == null) {
			String illegalChars = Config.getProperty("illegalAttachmentFilenameChars");
			if (illegalChars == null) {
				throw new IllegalStateException("missing illegalAttachmentFilenameChars in lendexconfig");
			}
			// String illegalChars = "\\/*?|;<>\"&'#";
			attachmentFilenameIndex = new ArrayList();
			for (int i = 0; i < illegalChars.length(); i++) {
				int cval = illegalChars.charAt(i);
				while (attachmentFilenameIndex.size() < cval + 1) {
					attachmentFilenameIndex.add(null);
				}
				attachmentFilenameIndex.set(cval, "_");
			}
		}
		return attachmentFilenameIndex;
	}
	
	/**
	 * consult Config file for invalid chars and ensure filename doesn't contain them
	 * @return false if illegal chars are found in file name
	 */
	public static boolean isValidFileName(final String s) {
		if (s == null)
			return false;
		String illegalChars = Config.getProperty("illegalAttachmentFilenameChars");
		if (illegalChars == null) {
			throw new IllegalStateException("missing illegalAttachmentFilenameChars in lendexconfig");
		}
		for (int i = 0; i < illegalChars.length(); i++) {
			int cval = illegalChars.charAt(i);
			if (s.indexOf(cval) > -1)
			    return false;
		}
		return true;
	}
	
	/**
	 * escapes chars using a list of replacement strings and returns resulting string
	 * we look up a replacement by using the char value as an index into teh list--
	 * if there is one there, use it, or else just put the char on
	 */	
	public static String escape(final String orig, List replacements) {
		StringBuilder buf = null;
		if (orig == null)
			return orig;	
		int len = orig.length();
		int size = replacements.size();
		for (int i = 0; i < len; i++) {
			int c = orig.charAt(i);
			if (c < size && replacements.get(c) != null) {
				// init strbuf if not initted
				if (buf == null) {
					buf = new StringBuilder(orig.substring(0, i));
				}
				buf.append(replacements.get(c));

			} else if (buf != null) {
				buf.append(orig.charAt(i));
			}
		}
		if (buf != null) {
			return buf.toString();
		}
		return orig;

	}
	
	
	/**
	 * escapes html entities and returns resulting string
	 * use unEscapeHtml to undo these changes
	 * 
	 * @param str
	 * @return
	 */
	public static final String escapeHtmlEntities(final String str) {
	    return escape(str, htmlEscapeIndex);
	} // escapeHtmlEntities

	/**
	 * escapes html entities , except the single quote and returns resulting string
	 * 
	 * 
	 * @param str
	 * @return
	 */
	public static final String escapeJavascriptEntities(final String str) {
	    return escape(str, javascriptEscapeIndex);
	} // escapeJavascriptEntities
	/**
	 * gets rid of bogus filename characters used in fax attachments
	 * @param str original filename
	 * @return
	 */
	public static final String escapeAttachmentFilenames(final String str) {
	    return escape(str, getAttachmentFilenameReplacements());
	}

	/**
	 * unescapes html entities. 
	 * 
	 * @param value
	 * @return value with escaped html enties unescaped
	 */
	public static final String unEscapeHtml(final String value) {
		
		if (isEmpty(value))
			return value;
					
		StringBuilder replaced = new StringBuilder(value);
		for (int j=0;j<HTML_ENTITIES.length; j++)
			replaced = replaceAll(replaced, HTML_ESC_ENTITIES[j], HTML_ENTITIES[j]);
		
		// change "nbsp;" to space
		replaced = replaceAll(replaced, "&nbsp;", " ");
		return replaced.toString();
	} // end unEscapeHtml
	
    /**
     * undo quoting used for "other" values in ifother
     * @param v
     * @return
     */
    public static String dequote(String v) {
        if (v.startsWith("\"") && v.endsWith("\"")) {
            v = v.substring(1, v.length() - 1);
            return v.replaceAll("\\\"", "\"");
        } else {
            return v;
        }
    }

    /**
     * do quoting used for "other" values in ifother
     * @param v
     * @return
     */
    public static String enquote(String v) {
        if (isEmpty(v))
            return v;        
        
        return '"' + v.replaceAll("\"", "\\\\\"") + '"';
    }
    
	/**
	 * Replaces all references of toReplace in text with char with. 
	 * @param text
	 * @param repl
	 * @param with
	 * @return replaced text String
	 */
	public static final StringBuilder replaceAll(
	                     final StringBuilder text, final String toReplace, final char with) {

		if (text == null || toReplace == null || toReplace.length() == 0)
			return text;
	   
		StringBuilder replaced = new StringBuilder(text.length());
		int start = 0, end = 0;
		while ((end = text.indexOf(toReplace, start)) != -1) {
			replaced.append(text.substring(start, end)).append(with);
			start = end + toReplace.length();
		}
		replaced.append(text.substring(start));
		
		return replaced;		
	} // end replaceAll	
	
	/**
	 * Replaces all references of toReplace in text with char with. 
	 * @param text
	 * @param repl
	 * @param with
	 * @return replaced text String
	 */
	public static final StringBuilder replaceAll(
	                     final StringBuilder text, final String toReplace, final String with) {

		if (text == null || toReplace == null || toReplace.length() == 0)
			return text;
	   
		StringBuilder replaced = new StringBuilder(text.length());
		int start = 0, end = 0;
		while ((end = text.indexOf(toReplace, start)) != -1) {
			replaced.append(text.substring(start, end)).append(with);
			start = end + toReplace.length();
		}
		replaced.append(text.substring(start));
		
		return replaced;		
	} // end replaceAll	
	
	
	public static final String replaceAll(final String text, final String toReplace, final String with) {
		StringBuilder buf = replaceAll(new StringBuilder(text), toReplace, with);
		if (buf != null)
			return buf.toString();
		return null;
	}	
	
	public static final String replaceAll(final String text, final String toReplace, final char with) {
		StringBuilder buf = replaceAll(new StringBuilder(text), toReplace, with);
		if (buf != null)
			return buf.toString();
		return null;
	}
	
	/**
	 * parses comma seperated string and returns trimmed non empty strings  
	 * 
	 * @param commaSeparated
	 * @return
	 */
	public static final List getCommaSeparatedNonEmptyStrings(final String commaSeparated) {
		
		List list = new ArrayList();
		if (isEmpty(commaSeparated))
			return list;
			
		StringTokenizer tokenizer = new StringTokenizer(commaSeparated, ",\r\n\t ");		
		while (tokenizer.hasMoreTokens()) {
			String token = tokenizer.nextToken().trim();
			if (!token.equals(ConstantRegistry.EMPTY_STRING))
				list.add(token);
		}

		return list;

	} // end getCommaSeparatedStrings
	
	public static final List getTokenSeparatedNonEmptyStrings(final String commaSeparated, final String tokenSep) {
		
		List list = new ArrayList();
		if (isEmpty(commaSeparated))
			return list;
			
		StringTokenizer tokenizer = new StringTokenizer(commaSeparated, tokenSep);		
		while (tokenizer.hasMoreTokens()) {
			String token = tokenizer.nextToken().trim();
			if (!token.equals(ConstantRegistry.EMPTY_STRING))
				list.add(token);
		}

		return list;

	} // end getCommaSeparatedStrings
	public static final boolean isEmpty(String string) 
	{
		return string == null || string.equals(ConstantRegistry.EMPTY_STRING); 
	}
	
	public static final String substring(String string, char delimeter) 
	{
		if (!isEmpty(string)) {
			int i = string.indexOf(delimeter);
			if (i != -1)
				return string.substring(0, i);
		}
		return null;
	}
	/**
	 * (!! vs) deprecated this method. this is very bed implementation of 
	 * html escaping.
	 * 
	 * @deprecated
	 * @param source
	 * @return
	 */
	public static String stripOffHTML(String source)
	{
		if(source.indexOf("<")!=-1)
		{
			source = source.replaceAll("\\<.*?>","");
		}
		return source;
	}

	public static String replace(String source, String from, String to)
	 {
	  String s = source;
 
	  if ( source.indexOf(from) != -1 )
	  {
	   StringBuilder sb = new StringBuilder();
 
	   int j = 0;
 
	   for ( int i = 0; (i = source.indexOf(from, j)) != -1; )
	   {
		sb.append(source.substring(j, i));
		sb.append(to);
		j = i + from.length();
	   }
 
	   sb.append(source.substring(j));
 
	   s = sb.toString();
	  }
 
	  return( s );
	 }

	/*
	 * Return a buffer representing the supplied text, in which all single quotes (')
	 * are escaped according to the specified nesting index
	 * 
	 * example: consider the following line:
	 * "onClick = 'javascript: printThis('my name is Martin O'Neil');'"
	 * the ' in the name "Martin O'Neil" is nested two levels deep (levelOfNesting=2)
	 * So we would call escapeNestedSingleQuotes("Martin O'Neil", 2);
	 */
	 public static StringBuilder escapeNestedSingleQuotes(String text, int levelOfNesting) {
	 	StringBuilder replacement = new StringBuilder();
	 	for (int i=0; i<levelOfNesting; i++)
	 		replacement.append("\\");
	 	replacement.append("&#39;");
	 	return replaceAll(new StringBuilder(text), "'", replacement.toString());
	 }	
	 
	 /**
	  * Prepares strings for use as javascript strings/arguments.
	  * Escapes
	  * <ul>
	  * 	<li>backslash</li>
	  * 	<li>single quote</li>
	  * 	<li>double quote</li> 
	  * @param text
	  * @return escaped text
	  */
	 
	 public static String escapeForJavascript(String text) {
		 String s = replaceAll(text, "\\", "\\\\");
		 //s = replaceAll(s, "\"", "&#34;");
		 s = replaceAll(s, "'", "\\'");
		 s = replaceAll(s, "\"", "\\\"");
		 //s = escapeNestedSingleQuotes(s, 1).toString();
		 return s;
	 }

	 /**
	  * Prepares strings for use in javascript dialogs alert, confirm.
	  * Escapes
	  * <ul>
	  * 	<li>backslash</li>
	  * 	<li>single quote</li>
	  * 	<li>double quote</li> 
	  * @param text
	  * @return escaped text
	  */

	 public static String escapeForJavascriptDialog(String text) {
		 String s = replaceAll(text, "\\", "\\\\");
		 s = replaceAll(s, "'", "\'");
		 s = replaceAll(s, "\"", "\\\"");
		 return s;
	 }
	 
	 
	/*
	 * Remove all linefeeds and tabs from string
	 */
	public static String unformat(String source){
		return source.replaceAll("[\t\r\n]", ConstantRegistry.EMPTY_STRING);
	}
	
	/*
	 * Make all line feeds of the same sort
	 */
	public static String consolidateLineFeeds(String source, String lineFeedToUse) {
		return source.replaceAll("[\r\n]", lineFeedToUse);
	}	
    
    /*
     * these are a bunch of routines I got off the net for a course...we can do search without 
     * creating lower case copies of all the param values
     */

    /**
     * look for the one string inside another (starting at index)
     * @param string
     * @param substring
     * @param fromIndex
     * @return
	 */
    public static int indexOfIgnoreCase(String string, String substring, int fromIndex) {
        for(int i = fromIndex; i < string.length(); i++) {
            if (startsWithIgnoreCase(string, substring, i)) {
                return i;
            }
        }
        return -1;
    }

    /**
     * look for the one string inside another
     * @param string
     * @param substring
     * @return
     */
    public static int indexOfIgnoreCase(String string, String substring) {
        return indexOfIgnoreCase(string, substring, 0);
    }

    public static boolean startsWithIgnoreCase(String string, String substring, int fromIndex) {
        if ((fromIndex < 0) || ((fromIndex + substring.length()) > string.length())) {
            return false;
        }
        
        
        for (int i = 0; i < substring.length(); i++){
        	int c1= Character.codePointAt(string,fromIndex+i);
        	if(c1 == 160)
        		c1=32;
        	int c2= Character.codePointAt(substring,i);
            if (Character.toUpperCase(c1) != 
                    Character.toUpperCase(c2)) {
                return false;
            }
        }
        return true;
    }

    public static boolean startsWithIgnoreCase(String string, String substring) {
        return startsWithIgnoreCase(string, substring, 0);
    }
    
    public static String getCSEmails(String[] recipients) {
       	String ret = ConstantRegistry.EMPTY_STRING;
    	int length = recipients.length;
    	for(int i = 0; i<length; i++) {
    		ret+=recipients[i];
    		if(i<length-1)
    			ret+=", ";
    	}
    	return ret;    	
    }
    /**
     * make it look pretty and error-like
     * 
     */
    static String wrapErrorMessage(String messageText) {
        StringBuilder sb = new StringBuilder(100);
        sb.append("<p /><span class='ErrorText' style=''>");
        sb.append(messageText);
        sb.append("</span>");
        return sb.toString();
    }
    
    /**
	 * construct an internal name from the given display name.
	 * strip out some non-alpha characters and convert to camelCase
	 * example: Toast and a Cup^o tea -> toastAndACupoTea
	 * TODO: max length check
	 * @param display name
	 * @return internal name
	 */
	public static String buildInternalName(String name) {
		String result = "";
		name = name.replaceAll("[~|!|@|$|%|&|#|*|`|?|\"|']*","");
		name = name.replaceAll("\\s+"," ");
		if (name.indexOf(' ') == -1) {
			result = name.toLowerCase();
		}
		else {
			String[] nameArray = name.split(" ");
			for (int i=0; i < nameArray.length; i++) {
				String temp = nameArray[i];
				if (result == "")
					result += temp.toLowerCase();
				else {
					result += temp.substring(0,1).toUpperCase();
					if (temp.length() > 1 ) {
						result += temp.substring(1).toLowerCase();
					}
				}
			}
		}
		return result;
	}

   public static String replaceApostrophe(String s){
	   String temp=null;
	   if(s != null)
		   temp = s.replaceAll("'", "&apos;");
	   return temp;
   }

   public static String removeStartingAndTrailingWildCards(String value) {
	   while(value.endsWith("*") || value.endsWith("?")){
		   value = value.substring(0, value.length()-1);
	   }	   
	   while(value.startsWith("*") || value.startsWith("?")){
		   value = value.substring(1, value.length());
	   }	   
	   return value;
   }
   
	/**
	 * parses comma seperated string and returns a set of trimmed non empty strings  
	 * 
	 * @param commaSeparated
	 * @return
	 */
	public static final Set getValueSetFromCommaSeparatedString(final String commaSeparated) {
		
		Set set = new HashSet();
		if (isEmpty(commaSeparated))
			return null;
			
		StringTokenizer tokenizer = new StringTokenizer(commaSeparated, ",");		
		while (tokenizer.hasMoreTokens()) {
			String token = tokenizer.nextToken().trim();
			if (ConstantRegistry.EMPTY_STRING.equals(token));
				set.add(token);
		}

		return set;

	} // end getCommaSeparatedStrings
	
	public static final String getTrimmedSubStringInUTF8(String string,int maxbytes) {
		String encoding = "UTF-8";
		 for (int index = 0, len = string.length(), bytes = 0; index < len; ++index) {
             try {
				bytes += String.valueOf(string.charAt(index)).getBytes(encoding).length;
				if (bytes > maxbytes){
                    return string.substring(0, index);
				}
			} catch (Exception e) {
				return string.substring(0,1000);
			}
             
     }
     return string;
	} 
}
